package com.bite.system.service.exam.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bite.common.core.constants.Constants;
import com.bite.common.core.enums.ResultCode;
import com.bite.common.security.exception.ServiceException;
import com.bite.system.domain.exam.Exam;
import com.bite.system.domain.exam.ExamQuestion;
import com.bite.system.domain.exam.dto.ExamAddDTO;
import com.bite.system.domain.exam.dto.ExamEditDTO;
import com.bite.system.domain.exam.dto.ExamQueryDto;
import com.bite.system.domain.exam.dto.ExamQuestionAddDTO;
import com.bite.system.domain.exam.vo.ExamDetailVO;
import com.bite.system.domain.exam.vo.ExamVO;
import com.bite.system.domain.question.Question;
import com.bite.system.domain.question.vo.QuestionVO;
import com.bite.system.manager.ExamCacheManager;
import com.bite.system.mapper.exam.ExamMapper;
import com.bite.system.mapper.exam.ExamQuestionMapper;
import com.bite.system.mapper.question.QuestionMapper;
import com.bite.system.service.exam.IExamService;
import com.github.pagehelper.PageHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Service
public class ExamServiceImpl extends ServiceImpl <ExamQuestionMapper,ExamQuestion> implements IExamService {
    @Autowired
    private ExamMapper examMapper;
    @Autowired
    private QuestionMapper questionMapper;
    @Autowired
    private ExamQuestionMapper examQuestionMapper;
    @Autowired
    private ExamCacheManager examCacheManager;

    @Override
    public List<ExamVO> list(ExamQueryDto examQueryDto) {
        PageHelper.startPage(examQueryDto.getPageNum(),examQueryDto.getPageSize());
        //question表太多的可变，导致他的SQL会有些复杂，而且维护麻烦
        List<ExamVO> questionVOList =examMapper.selectExamList(examQueryDto);
        return questionVOList ;
    }

    @Override
    public String add(ExamAddDTO examAddDto) {
        checkExamSaveParams(examAddDto,null);
        Exam exam=new Exam();
       // checkExam(exam);
        BeanUtil.copyProperties(examAddDto,exam);
        System.out.println("*************---------------"+exam.getExamId());
        examMapper.insert(exam);
        return exam.getExamId().toString();
    }

    @Override
    public boolean questionAdd(ExamQuestionAddDTO examQuestionAddDTO) {
        Exam exam=getExam(examQuestionAddDTO.getExamId());
        checkExam(exam);
        if(Constants.TRUE.equals(exam.getStatus())){
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        Set<Long>questionIdSet=examQuestionAddDTO.getQuestionIdSet();
        if(CollectionUtils.isEmpty(questionIdSet)){
            return false;
        }
        List<Question>questionList=questionMapper.selectByIds(questionIdSet);
        if(CollectionUtils.isEmpty(questionIdSet)||questionList.size()<questionIdSet.size()){
            throw new ServiceException(ResultCode.EXAM_QUESTION_NOT_EXIST);
        }
      return insertExamQuestion(exam,questionIdSet);
    }

    @Override
    public  ExamDetailVO  detail(Long examId) {
        //先查询竞赛信息
        ExamDetailVO examDetailVO=new ExamDetailVO();
        Exam exam=getExam(examId);
        BeanUtil.copyProperties(exam,examDetailVO);
        //一个竞赛对应多个问题
        List<ExamQuestion> examQuestionList= examQuestionMapper
                .selectList(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId,examId)
                .orderByAsc(ExamQuestion::getQuestionOrder));
        if(CollectionUtils.isEmpty(examQuestionList)){
            //返回详情  只包含竞赛的基本信息
            return examDetailVO;
        }
        //这就说明，包含题目的信息,将当前竞赛的全部id放到一个集合中，这个::就相当于一个lambda的表达式，相当于是来遍历啥的
        List<Long>questionIdList=examQuestionList.stream().map(ExamQuestion::getQuestionId).toList();
        //Select * from tb_question question_id in(1，2，3) 这个in相当于,第一个参数相当于in之前的属性，后面代表括号那些数据
        List<Question>questionList= questionMapper.selectList(new LambdaQueryWrapper<Question>()
                        .select(Question::getQuestionId,Question::getTitle,Question::getDifficulty)
                .in(Question::getQuestionId,questionIdList));
        //第一个是要转的，第二个是想转成什么类型
        List<QuestionVO>questionVOList= BeanUtil.copyToList(questionList,QuestionVO.class);
        examDetailVO.setExamQuestionList(questionVOList);
        return examDetailVO;
    }
    @Override
    public int edit(ExamEditDTO examEditDTO) {

        checkExamSaveParams(examEditDTO,examEditDTO.getExamId());
        Exam exam=getExam(examEditDTO.getExamId());
        if(Constants.TRUE.equals(exam.getStatus())){
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        checkExam(exam);
        //竞赛的标题是否重复的判断，判断开始时间和结束时间是否不符合常理
        exam.setTitle(examEditDTO.getTitle());
        exam.setStartTime(exam.getStartTime());
        exam.setEndTime(exam.getEndTime());
        return examMapper.updateById(exam);
    }

    @Override
    public int questionDelete(Long examId, Long questionId) {
        Exam exam=getExam(examId);
        checkExam(exam);
        return examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId,examId)
                .eq(ExamQuestion::getQuestionId,questionId));
    }

    @Override
    public int delete(Long examId) {
       Exam exam=getExam(examId);
       checkExam(exam);
        if(Constants.TRUE.equals(exam.getStatus())){
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
     //有的竞赛可能没有题目，假如此时返回这个就不合适了，因为我们是判断行数
       examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
               .eq(ExamQuestion::getExamId,examId));
        return   examMapper.deleteById(examId);
    }
//发布的时候，注意缓存，后期我们需要对缓存有一些淘汰对策略，假如你发布就去添加缓存，那要是没人查啥的呢，那不就浪费了
    //不如直接查询对时候添加缓存。
    @Override
    public int publish(Long examId) {
        //竞赛是否存在
        Exam exam=getExam(examId);
        //当前竞赛题目个数 select count(0) from tb_exam_question where exam_id=#{examId}
        //假如竞赛对完赛时间比当前时间的前面，换句话就是之前的竞赛，相当于王者荣耀历史记录
        if(exam.getEndTime().isBefore(LocalDateTime.now())){
            throw  new ServiceException(ResultCode.EXAM_IS_FINISHED);
        }

        Long count= examQuestionMapper.selectCount(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId,examId));
        //看他有多少条，是否不包含题目
        if(count==null||count<=0){
            throw new ServiceException(ResultCode.EXAM_NOT_HAS_QUESTION);
        }
        //可以进行发布了
        exam.setStatus(Constants.TRUE);
       //将新发布的竞赛数据存储到redis，e:t:l      e:d:examId对redis对操作并不是很简单对操作
        examCacheManager.addCache(exam);

        return  examMapper.updateById(exam);
    }

    @Override
    public int cancelPublish(Long examId) {
        //已经发布的竞赛，我们不能撤销，因为还是对于农来说，这个是冗余
        Exam exam=getExam(examId);
        checkExam(exam);
        if(exam.getEndTime().isBefore(LocalDateTime.now())){
            throw new ServiceException(ResultCode.EXAM_IS_FINISHED);
        }
        exam.setStatus(Constants.FALSE);
        examCacheManager.deleteCache(examId);
        return examMapper.updateById(exam);
    }

    public void checkExam(Exam exam){
        if(exam.getStartTime().isBefore(LocalDateTime.now())){
            throw new ServiceException(ResultCode.EXAM_STARTED);
        }
    }

    public  void checkExamSaveParams(ExamAddDTO examSaveDTO,Long examId){
        //1.竞赛标题是否重复，2.竞赛开始的结束于开始时间不合理的判断
        List <Exam>examList=examMapper.selectList(new LambdaQueryWrapper<Exam>()
                .eq(Exam::getTitle,examSaveDTO.getTitle())
                .ne(examId!=null,Exam::getExamId,examId) );
        if(CollectionUtils.isNotEmpty(examList)){
            throw new ServiceException(ResultCode.FAILED_ALREADY_EXISTS);
        }

        if(examSaveDTO.getStartTime().isBefore(LocalDateTime.now())){
            throw new ServiceException(ResultCode.EXAM_START_TIME_BEFORE_CURRENT_TIME);
        }
        if(examSaveDTO.getStartTime().isAfter(examSaveDTO.getEndTime())){
            throw new ServiceException(ResultCode.EXAM_END_TIME_AFTER_CURRENT_TIME);
        }
    }
    public boolean  insertExamQuestion(Exam exam,Set<Long>questionIdSet){
        int num=1;
        List<ExamQuestion>examQuestionList=new ArrayList<>();
        //for循环里面，他的数据库操作有可能过于繁重，所以还是把他们优化提出来。
        for(Long questionId:questionIdSet){
            //判断question是否为空
//            Question question=questionMapper.selectById(questionId);
//            if(question==null){
//                throw new ServiceException(ResultCode.EXAM_QUESTION_NOT_EXIST);
//            }
            ExamQuestion examQuestion=new ExamQuestion();
            examQuestion.setQuestionId(questionId);
            examQuestion.setExamId(exam.getExamId());
            examQuestion.setQuestionOrder(num++);
            examQuestionList.add(examQuestion);
        }
       boolean a= saveBatch(examQuestionList);
        return a;
    }


    public Exam getExam(Long examId){
        Exam exam=examMapper.selectById(examId);
        if(exam==null){
            throw  new ServiceException(ResultCode.FAILED_NOT_EXISTS);
        }
        return exam;
    }

    public static void main(String[] args) {

    }

}
